package com.tianji.learning.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.tianji.learning.enums.LessonStatus.LEARNING;
import static com.tianji.learning.enums.LessonStatus.NOT_BEGIN;
import static com.tianji.learning.enums.PlanStatus.PLAN_RUNNING;

@SuppressWarnings("ALL")
@Service
@RequiredArgsConstructor
@Slf4j
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;

    private final CatalogueClient catalogueClient;

    private final LearningRecordMapper learningRecordMapper;

    /**
     * 批量添加课程到课表中
     * @param userId  用户id
     * @param courseIds  课程id集
     */
    @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 1.查询课程有效期
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在，无法添加
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        // 2.循环遍历，处理LearningLesson数据
        List<LearningLesson> list = new ArrayList<>(cInfoList.size());
        for (CourseSimpleInfoDTO cInfo : cInfoList) {
            LearningLesson lesson = new LearningLesson();
            // 2.1.获取过期时间
            Integer validDuration = cInfo.getValidDuration();
            if (validDuration != null && validDuration > 0) {
                LocalDateTime now = LocalDateTime.now();
                lesson.setCreateTime(now);
                lesson.setExpireTime(now.plusMonths(validDuration));
            }
            // 2.2.填充userId和courseId
            lesson.setUserId(userId);
            lesson.setCourseId(cInfo.getId());
            list.add(lesson);
        }
        // 3.批量新增
        saveBatch(list);
    }


    /**
     * 分页查询我的课表
     * @param query
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.分页查询
        // select * from learning_lesson where user_id = #{userId} order by latest_learn_time limit 0, 5
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, userId) // where user_id = #{userId}
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        // 3.查询课程信息
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);

        // 4.封装VO返回
        List<LearningLessonVO> list = new ArrayList<>(records.size());
        // 4.1.循环遍历，把LearningLesson转为VO
        for (LearningLesson r : records) {
            // 4.2.拷贝基础属性到vo
            LearningLessonVO vo = BeanUtils.copyBean(r, LearningLessonVO.class);
            // 4.3.获取课程信息，填充到vo
            CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
            vo.setCourseName(cInfo.getName());
            vo.setCourseCoverUrl(cInfo.getCoverUrl());
            vo.setSections(cInfo.getSectionNum());
            list.add(vo);
        }
        return PageDTO.of(page, list);
    }

    private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
        // 3.1.获取课程id
        Set<Long> cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        // 3.2.查询课程信息
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(cIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在，无法添加
            throw new BadRequestException("课程信息不存在！");
        }
        // 3.3.把课程集合处理成Map，key是courseId，值是course本身
        Map<Long, CourseSimpleInfoDTO> cMap = cInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        return cMap;
    }

    /**
     * 查询我正在学习的课程
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        // 1.获取当前登录的用户
        Long userId = UserContext.getUser();
        // 2.查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (lesson == null) {
            return null;
        }
        // 3.拷贝PO基础属性到VO
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        // 4.查询课程信息
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (cInfo == null) {
            throw new BadRequestException("课程不存在");
        }
        vo.setCourseName(cInfo.getName());
        vo.setCourseCoverUrl(cInfo.getCoverUrl());
        vo.setSections(cInfo.getSectionNum());
        // 5.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer courseAmount = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(courseAmount);
        // 6.查询小节信息
        List<CataSimpleInfoDTO> cataInfos =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        if (!CollUtils.isEmpty(cataInfos)) {
            CataSimpleInfoDTO cataInfo = cataInfos.get(0);
            vo.setLatestSectionName(cataInfo.getName());
            vo.setLatestSectionIndex(cataInfo.getCIndex());
        }
        return vo;
    }

    /**
     * @param courseId
     * @Description 删除我的课程
     * @Author 老李头儿
     * @Date 2025年03月22 21:23:46
     * @Return
     **/
    @Override
    public void deleteCourseFromLesson(Long userId, Long courseId) {
        // 1.获取当前登录用户
        if (userId == null){
            userId = UserContext.getUser();
        }

        // 2.删除我的课表
        this.lambdaUpdate()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .remove();
    }

    /**
     * 校验当前用户是否可以学习当前课程
     *
     * @param courseId 课程id
     * @return lessonId，如果是报名了则返回lessonId，否则返回空
     */
    @Override
    public Long isLessonValid(Long courseId) {
        //获取当前用户
        Long userId = UserContext.getUser();
        // 判断该用户课表中是否有该课程
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (ObjectUtil.isEmpty(lesson)){
            return null;
        }

        // 判断该课程是否过期
        if (lesson.getExpireTime().isBefore(LocalDateTime.now())){
            return null;
        }
        return lesson.getId();
    }

    /**
     * 统计课程学习人数
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        return lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        NOT_BEGIN.getValue(),
                        LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();
    }

    /**
     * 查询指定课程信息
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();

        // 2.查询课程信息
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        LearningLesson lesson = getOne(queryWrapper);

        if (lesson == null) {
            return null;
        }

        // 3.处理VO
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    /**
     * 创建学习计划
     *
     * @param courseId 课程id
     * @param freq     学习频率
     */
    @Override
    public void createLearningPlan(LearningPlanDTO planDTO) {
        // 1.获取当前登录的用户
        Long userId = UserContext.getUser();
        // 2.查询课表中的指定课程有关的数据
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, planDTO.getCourseId())
                .one();
        AssertUtils.isNotNull(lesson, "课程信息不存在！");
        // 3.修改数据
        // 2、更新学习计划数据
        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq, planDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PLAN_RUNNING)
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

    /**
     * 查询我的学习计划
     *
     * @param query
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery pageQuery) {
        //定义返回值
        LearningPlanPageVO vo = new LearningPlanPageVO();
        //获取用户id
        Long userId = UserContext.getUser();
        //分页查询学习计划
        Page<LearningLesson> pageResult  = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PLAN_RUNNING)
                .in(LearningLesson::getStatus, NOT_BEGIN.getValue(), LEARNING.getValue())
                .page(pageQuery.toMpPage("latest_learn_time", false));

        List<LearningLesson> records = pageResult.getRecords();
        if(ObjectUtil.isEmpty(records)){
            return vo.emptyPage(pageResult);
        }

        // 3、根据课程ID集合查询课程信息
        Map<Long, CourseSimpleInfoDTO> couseInfoMap = queryCouseInfoAnd2Map(records);

        // 4.1、获取本周开始、结束日期
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());

        //获取本周学习的的课程
        QueryWrapper<LearningRecord> query = new QueryWrapper<>();
        query
                .select("lesson_id,count(1) num")
                .eq("user_id",userId)
                .eq("finished",true)
                .between("finish_time",begin,end)
                .groupBy("lesson_id");

        List<Map<String,Object>> mapList = this.learningRecordMapper
                .selectMaps(query);
        Map<Long, Long> learedSectionNumMap = mapList.stream()
                .collect(Collectors.toMap(m -> (Long) m.get("lesson_id"), m -> (Long) m.get("num")));


        ArrayList<LearningPlanVO> learningPlanVOS = new ArrayList<>(records.size());
        //遍历所有的学习计划
        for (LearningLesson record : records) {
            //拷贝属性
            LearningPlanVO planVO = BeanUtils.copyBean(record, LearningPlanVO.class);
            //填充属性
            CourseSimpleInfoDTO courseInfo = couseInfoMap.get(record.getCourseId());
            if(ObjectUtil.isNotEmpty(courseInfo)){
                planVO.setCourseName(courseInfo.getName());
                planVO.setSections(courseInfo.getSectionNum());
            }

            // 5.3、填充本周已学习小节数
            planVO.setWeekLearnedSections(learedSectionNumMap.getOrDefault(record.getId(),0L).intValue());

            //添加到集合
            learningPlanVOS.add(planVO);

            // 6、计算本周计划学习的总小节数
            // select sum(week_freq) from learning_lesson where user_id = #{userId} and plan_status = 1 and status in (0, 1)
            LearningLesson lesson = this.query()
                    .select("sum(week_freq) as total_weekfreq")
                    .eq("user_id", userId)
                    .eq("plan_status", PLAN_RUNNING)
                    .in("status", NOT_BEGIN, LEARNING)
                    .one();

            // 7、计算本周已学习的总小节数
            long sum = learedSectionNumMap.values().stream().mapToLong(Long::longValue).sum();
            Integer weekFinished = Long.valueOf(sum).intValue();

            // 8、封装Vo并返回
            vo.setWeekTotalPlan(lesson.getTotalWeekfreq());
            vo.setWeekFinished(weekFinished);
            return vo.pageInfo(pageResult.getTotal(), pageResult.getPages(), learningPlanVOS);
        }
        return null;
    }

    private Map<Long, CourseSimpleInfoDTO> queryCouseInfoAnd2Map(List<LearningLesson> records) {
        // 3.1、收集当前页码的所有课程ID集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());

        // 3.2、发起远程调用
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);

        // 3.3、健壮性判断
        if(ObjectUtil.isEmpty(courseInfoList)){
            throw new BizIllegalException("课程信息不存在，无法查询课表");
        }

        // 3.4、将list转为map，供后续高效使用
        Map<Long, CourseSimpleInfoDTO> couseInfoMap = courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        return couseInfoMap;
    }


}